package com.zwf.checkstyledemo1.checks;

import com.puppycrawl.tools.checkstyle.api.*;
import java.util.*;

public class DivisionByZeroCheck extends AbstractCheck {
    private final Set<String> zeroVariables = new HashSet<>();
    private final Map<DetailAST, String> delayedDivisions = new HashMap<>();
    private final Set<Integer> reportedLiteralDivisions = new HashSet<>();

    @Override
    public int[] getDefaultTokens() {
        return getRequiredTokens();
    }

    @Override
    public int[] getAcceptableTokens() {
        return getRequiredTokens();
    }

    @Override
    public int[] getRequiredTokens() {
        return new int[] {
                TokenTypes.DIV,
                TokenTypes.ASSIGN,
                TokenTypes.VARIABLE_DEF
        };
    }

    @Override
    public void beginTree(DetailAST rootAST) {
        zeroVariables.clear();
        delayedDivisions.clear();
        reportedLiteralDivisions.clear();
    }

    @Override
    public void finishTree(DetailAST rootAST) {
        // 处理所有延迟的除法检查
        for (Map.Entry<DetailAST, String> entry : delayedDivisions.entrySet()) {
            DetailAST divAst = entry.getKey();
            String varName = entry.getValue();
            if (zeroVariables.contains(varName)) {
                reportDivisionByZero(divAst, "变量 '" + varName + "' (值为0)");
            }
        }
    }

    @Override
    public void visitToken(DetailAST ast) {
        switch (ast.getType()) {
            case TokenTypes.ASSIGN:
                processAssignment(ast);
                break;
            case TokenTypes.VARIABLE_DEF:
                processVariableDef(ast);
                break;
            case TokenTypes.DIV:
                processDivision(ast);
                break;
        }
    }

    private void processAssignment(DetailAST assignAst) {
        DetailAST lhs = assignAst.getFirstChild();
        DetailAST rhs = lhs != null ? lhs.getNextSibling() : null;

        if (lhs != null && lhs.getType() == TokenTypes.IDENT && isEffectivelyZero(rhs)) {
            zeroVariables.add(lhs.getText());
        }
    }

    private void processVariableDef(DetailAST varDefAst) {
        DetailAST assign = varDefAst.findFirstToken(TokenTypes.ASSIGN);
        if (assign != null) {
            DetailAST lhs = assign.getFirstChild();
            DetailAST rhs = lhs != null ? lhs.getNextSibling() : null;
            if (lhs != null && lhs.getType() == TokenTypes.IDENT && isEffectivelyZero(rhs)) {
                zeroVariables.add(lhs.getText());
            }
        } else if (isClassField(varDefAst) && isNumericType(varDefAst)) {
            DetailAST ident = varDefAst.findFirstToken(TokenTypes.IDENT);
            if (ident != null) {
                zeroVariables.add(ident.getText());
            }
        }
    }

    private boolean isClassField(DetailAST varDefAst) {
        return varDefAst.getParent().getType() == TokenTypes.OBJBLOCK;
    }

    private boolean isNumericType(DetailAST varDefAst) {
        DetailAST typeAst = varDefAst.findFirstToken(TokenTypes.TYPE);
        if (typeAst != null) {
            String typeName = typeAst.getFirstChild().getText();
            return typeName.matches("int|long|float|double");
        }
        return false;
    }

    private void processDivision(DetailAST divAst) {
        DetailAST left = divAst.getFirstChild();
        DetailAST denominator = left != null ? left.getNextSibling() : null;
        if (denominator == null) return;

        // 处理字面量0除法（立即报告）
        if (isZeroLiteral(denominator)) {
            int lineNo = divAst.getLineNo();
            if (!reportedLiteralDivisions.contains(lineNo)) {
                reportedLiteralDivisions.add(lineNo);
                reportDivisionByZero(divAst, "字面量0");
            }
            return;
        }

        // 变量除法延迟到树遍历结束时处理
        if (denominator.getType() == TokenTypes.IDENT) {
            String varName = denominator.getText();
            delayedDivisions.put(divAst, varName);
        }
    }

    private boolean isEffectivelyZero(DetailAST expr) {
        if (expr == null) return false;

        // 处理括号表达式
        if (expr.getType() == TokenTypes.LPAREN) {
            DetailAST inner = expr.getFirstChild();
            return inner != null && isEffectivelyZero(inner);
        }
        return isZeroLiteral(expr);
    }

    private boolean isZeroLiteral(DetailAST ast) {
        if (ast == null) return false;
        int type = ast.getType();

        switch (type) {
            case TokenTypes.NUM_INT:
                return "0".equals(ast.getText());
            case TokenTypes.NUM_DOUBLE:
                String doubleText = ast.getText();
                return doubleText.equals("0.0") || doubleText.equals("0.");
            case TokenTypes.NUM_FLOAT:
                String floatText = ast.getText();
                return floatText.equals("0.0f") || floatText.equals("0f") || floatText.equals("0.F");
            case TokenTypes.NUM_LONG:
                return ast.getText().equals("0L");
            default:
                return false;
        }
    }

    private void reportDivisionByZero(DetailAST ast, String reason) {
        log(ast, "除零异常风险: 检测到除以" + reason);
    }
}